package cn.chencq.utils.redis;

import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;

import com.google.gson.Gson;

/**
 * Redis工具类
 * 
 * @author chencq
 * @email chenchaoqun@incar100.com
 */
@Component
public class RedisUtils {
	@Autowired
	private RedisTemplate<String, Object> redisTemplate;
	@Autowired
	private ValueOperations<String, String> valueOperations;
	@Autowired
	private HashOperations<String, String, Object> hashOperations;
	@Autowired
	private ListOperations<String, Object> listOperations;
	@Autowired
	private SetOperations<String, Object> setOperations;
	@Autowired
	private ZSetOperations<String, Object> zSetOperations;
	/** 默认过期时长，单位：秒 */
	public final static long DEFAULT_EXPIRE = 60 * 60 * 24;
	/** 不设置过期时长 */
	public final static long NOT_EXPIRE = -1;
	private final static Gson gson = new Gson();

	/**
	 * k-v
	 * 
	 * @Title: set
	 * @param key
	 * @param value
	 * @param expire
	 */
	public void set(String key, Object value, long expire) {
		valueOperations.set(key, toJson(value));
		if (expire != NOT_EXPIRE) {
			redisTemplate.expire(key, expire, TimeUnit.SECONDS);
		}
	}

	public void set(String key, Object value) {
		set(key, value, NOT_EXPIRE);
	}

	public <T> T get(String key, Class<T> clazz, long expire) {
		String value = valueOperations.get(key);
		if (expire != NOT_EXPIRE) {
			redisTemplate.expire(key, expire, TimeUnit.SECONDS);
		}
		return value == null ? null : fromJson(value, clazz);
	}

	public <T> T get(String key, Class<T> clazz) {
		return get(key, clazz, NOT_EXPIRE);
	}

	public String get(String key, long expire) {
		String value = valueOperations.get(key);
		if (expire != NOT_EXPIRE) {
			redisTemplate.expire(key, expire, TimeUnit.SECONDS);
		}
		return value;
	}

	/**
	 * 获取key对应的value
	 * 
	 * @Title: get
	 * @param key
	 * @return String
	 */
	public String get(String key) {
		return get(key, NOT_EXPIRE);
	}

	/**
	 * 
	 * 判断key是否存在
	 * 
	 * @Title: exists
	 * @param key
	 * @return boolean
	 */
	public boolean exists(String key) {
		return redisTemplate.hasKey(key);
	}

	/**
	 * 删除某个key
	 * 
	 * @Title: delete
	 * @param key
	 *            void
	 */
	public void delete(String key) {
		redisTemplate.delete(key);
	}

	/**
	 * 
	 * 返回key存储value的类型,包括none,string,list,set,zset,hash
	 * 
	 * @Title: type
	 * @param key
	 * @return String
	 */
	public String type(String key) {
		return redisTemplate.type(key).code();
	}

	/**
	 * 设置key的过期时间
	 * 
	 * @Title: expire
	 * @param key
	 * @param seconds
	 *            超时时间 单位：秒
	 * @return boolean
	 */
	public boolean expire(String key, int seconds) {
		return redisTemplate.expire(key, seconds, TimeUnit.SECONDS);
	}

	/**
	 * 
	 * 设置key在什么时间过期
	 * 
	 * @Title: expireAt
	 * @param key
	 * @param date
	 * @return boolean
	 */
	public boolean expireAt(String key, Date date) {
		return redisTemplate.expireAt(key, date);
	}

	/**
	 * 返回key剩余时间,单位:秒
	 * 
	 * @Title: ttl
	 * @param key
	 * @return long
	 */
	public long ttl(String key) {
		return redisTemplate.getExpire(key, TimeUnit.SECONDS);
	}

	/**
	 * 
	 * 设置key指定哈希集合中指定字段的值
	 * 
	 * @Title: hset
	 * @param key
	 * @param field
	 * @param value
	 *            void
	 */
	public void hset(String key, String field, String value) {
		hashOperations.put(key, field, value);
	}

	/**
	 * 设置key指定哈希集合中指定字段的值
	 * 
	 * @Title: hmset
	 * @param key
	 * @param map
	 *            void
	 */
	public void hmset(String key, Map<String, String> map) {
		hashOperations.putAll(key, map);
	}

	/**
	 * 
	 * 获取key指定哈希集合中指定字段的值
	 * 
	 * @Title: hget
	 * @param key
	 * @param field
	 * @return String
	 */
	public String hget(String key, String field) {
		if (exists(key)) {
			Object value = hashOperations.get(key, field);
			return String.valueOf(value);
		}
		return null;
	}

	/**
	 * 
	 * 从key指定的哈希集合中删除指定的域
	 * 
	 * @Title: hdel
	 * @param key
	 * @param field
	 * @return Long
	 */
	public long hdel(String key, String... field) {
		return hashOperations.delete(key, field);
	}

	/**
	 * 
	 * 获取key指定哈希集合包含的字段数量
	 * 
	 * @Title: hlen
	 * @param key
	 * @return Long
	 */
	public long hlen(String key) {
		return hashOperations.size(key);
	}

	/**
	 * 
	 * 获取key指定的哈希集合中所有的字段名称
	 * 
	 * @Title: hkeys
	 * @param key
	 * @return Set<String>
	 */
	public Set<String> hkeys(String key) {
		return hashOperations.keys(key);
	}

	/**
	 * 获取key指定的哈希集合中所有的字段的值
	 * 
	 * @Title: hvalues
	 * @param key
	 * @return List<?>
	 */
	public List<?> hvalues(String key) {
		return hashOperations.values(key);
	}

	/**
	 * 返回 key 指定的哈希集中所有的字段和值。 返回值中，每个字段名的下一个是它的值，所以返回值的长度是哈希集大小的两倍
	 * 
	 * 
	 * @param key
	 * @return 哈希集中字段和值的列表。当 key 指定的哈希集不存在时返回空列表。
	 */
	public Map<String, Object> hgetAll(String key) {
		return hashOperations.entries(key);
	}

	/**
	 * 从队列的右边入队一个或多个元素
	 * 
	 * 
	 * @param key
	 * @param values
	 * @return 入队后的队列长度
	 */
	public long rpush(String key, String... values) {
		return listOperations.rightPushAll(key, values);
	}

	/**
	 * 从队列的左边入队一个或多个元素
	 * 
	 * 
	 * @param key
	 * @param values
	 * @return 入队后的队列长度
	 */
	public long lpush(String key, String... values) {
		return listOperations.rightPushAll(key, values);
	}

	/**
	 * 获得队列(List)的长度
	 * 
	 * 
	 * @param key
	 * @return
	 */
	public long llen(String key) {
		return listOperations.size(key);
	}

	/**
	 * 返回指定位置范围内的元素列表
	 * 
	 * 
	 * @param key
	 * @param start
	 * @param end
	 * @return
	 */
	public List<?> lrange(String key, long start, long end) {
		return listOperations.range(key, start, end);
	}

	/**
	 * 修剪掉指定范围内的清单
	 * 
	 * 
	 * @param key
	 * @param start
	 * @param end
	 * @return
	 */
	public void ltrim(String key, long start, long end) {
		listOperations.trim(key, start, end);
	}

	/**
	 * 返回索引位置的元素
	 * 
	 * 
	 * @param key
	 * @param index
	 * @return 请求的元素，如果索引超过队列的范围返回nil
	 */
	public Object lindex(String key, long index) {
		return listOperations.index(key, index);
	}

	/**
	 * 设置队列里面指定位置元素的值,指定的位置超过队列的范围，就发生错误
	 * 
	 * 
	 * @param key
	 * @param index
	 * @param value
	 * @return
	 */
	public void lset(String key, long index, String value) {
		listOperations.set(key, index, value);
	}

	/**
	 * 删除队列中指定的元素
	 * 
	 * 
	 * @param key
	 * @param count
	 *            删除的次数及方向 <li>count<0 从队列的头开始删除，删除count的绝对值次</li> <li>count>0
	 *            从队列的尾部开始删除，删除count次</li> <li>count==0 删除全部</li>
	 * @param value
	 * @return 删除元素的个数
	 */
	public long lrem(String key, long count, String value) {
		return listOperations.remove(key, count, value);
	}

	/**
	 * 从队列的左边出队一个元素
	 * 
	 * 
	 * @param key
	 * @return 队列的第一个元素，如果key不存在返回nil
	 */
	public Object lpop(String key) {
		return listOperations.leftPop(key);
	}

	/**
	 * 从队列的右边出队一个元素
	 * 
	 * 
	 * @param key
	 * @return 队列的最后一个元素，如果key不存在，返回nil
	 */
	public Object rpop(String key) {
		return listOperations.rightPop(key);
	}

	/**
	 * 向set中添加成员
	 * 
	 * 
	 * @param key
	 * @param members
	 * @return 1 如果key被添加;0 如果key没有被添加
	 */
	public long sadd(String key, String... members) {
		return setOperations.add(key, members);
	}

	/**
	 * 将源key中的成员移到目标key中。该方法不支持按源key与目标key不在同一个redis的操作。
	 * 
	 * @param srckey
	 * @param destKey
	 * @param member
	 * @return boolean
	 */
	public boolean smove(String srckey, String destKey, String member) {
		return setOperations.move(srckey, member, destKey);
	}

	/**
	 * 对key列表求交集
	 * 
	 * @param keys
	 * @return
	 */
	public Set<Object> sinter(String key, Set<String> otherKeys) {
		if (key == null || otherKeys == null || otherKeys.size() == 0) {
			return new HashSet<Object>();
		}
		return setOperations.intersect(key, otherKeys);
	}

	/**
	 * 求交集并将交集结果存储到目标key中，如果目标key存在，目标key中的内容被覆盖。暂时不支持keys分布到多个机器
	 * 
	 * @param dstkey
	 * @param keys
	 * @return 交集的结果大小
	 */
	public long sinterstore(String key, Set<String> otherKeys, String destKey) {
		if (key == null || otherKeys == null || otherKeys.size() == 0) {
			return 0L;
		}
		return setOperations.intersectAndStore(key, otherKeys, destKey);
	}

	/**
	 * 对key列表求并集，暂不支持keys分布不同机器
	 * 
	 * @param keys
	 * @return
	 */
	public Set<Object> sunion(String key, Set<String> otherKeys) {
		if (key == null || otherKeys == null || otherKeys.size() == 0) {
			return new HashSet<Object>();
		}
		return setOperations.union(key, otherKeys);
	}

	/**
	 * 对key列表求并集，并将结果存储到目标key中，暂不支持keys分布不同机器
	 * 
	 * @param dstkey
	 * @param keys
	 * @return
	 */
	public long sunionstore(String key, Set<String> otherKeys, String destKey) {
		if (key == null || otherKeys == null || otherKeys.size() == 0) {
			return 0L;
		}
		return setOperations.unionAndStore(key, otherKeys, destKey);
	}

	/**
	 * 求keys列表的差集，暂不支持keys分布到不同的机器
	 * 
	 * @param keys
	 * @return
	 */
	public Set<Object> sdiff(String key, Set<String> otherKeys) {
		if (key == null || otherKeys == null || otherKeys.size() == 0) {
			return new HashSet<Object>();
		}
		return setOperations.difference(key, otherKeys);
	}

	/**
	 * 求keys列表的差集并存储到目标key，暂不支持keys分布到不同的机器
	 * 
	 * @param dstkey
	 * @param keys
	 * @return
	 */
	public long sdiffstore(String key, Set<String> otherKeys, String destKey) {
		if (key == null || otherKeys == null || otherKeys.size() == 0) {
			return 0L;
		}
		return setOperations.differenceAndStore(key, otherKeys, destKey);
	}

	/**
	 * 返回该set中所有的成员列表
	 * 
	 * 
	 * @param key
	 * @return 如果该key不存在返回nil
	 */
	public Set<Object> smembers(String key) {
		return setOperations.members(key);
	}

	/**
	 * 移除set中指定的成员列表，如果该成员不存在忽略，如果该key的类型不是set将发生错误
	 * 
	 * 
	 * @param key
	 * @param member
	 * @return 移除的成员个数，不存在的成员不计数
	 */
	public long srem(String key, String... member) {
		return setOperations.remove(key, member);
	}

	/**
	 * 随机返回并删除一个成员
	 * 
	 * 
	 * @param key
	 * @return 被移除的成员，如果成员不存在返回nil
	 */
	public Object spop(String key) {
		return setOperations.pop(key);
	}

	/**
	 * 计算该set中的成员个数
	 * 
	 * 
	 * @param key
	 * @return 成员个数，该key不存在返回0
	 */
	public long scard(String key) {
		return setOperations.size(key);
	}

	/**
	 * 判断指定的成员是否存储在key中
	 * 
	 * 
	 * @param key
	 * @param member
	 * @return
	 */
	public boolean sismember(String key, Object member) {
		return setOperations.isMember(key, member);
	}

	/**
	 * 从set中随机返回一个元素<br>
	 * 
	 * @param key
	 * @return
	 */
	public Object srandmember(String key) {
		return setOperations.randomMember(key);
	}

	/**
	 * 从set中随机返回num个元素<br>
	 * 
	 * @param key
	 * @param num
	 * @return
	 */
	public List<Object> srandmember(String key, long count) {
		return setOperations.randomMembers(key, count);
	}

	/**
	 * 该命令添加指定的成员到key对应的有序集合中，每个成员都有一个分数。 你可以指定多个分数/成员组合(jedis不支持)。 <li>
	 * 如果一个指定的成员已经在对应的有序集合中了，那么其分数就会被更新成最新的，并且该成员会重新调整到正确的位置，以确保集合有序。</li> <li>
	 * 如果key不存在，就会创建一个含有这些成员的有序集合，就好像往一个空的集合中添加一样。</li> <li>
	 * 如果key存在，但是它并不是一个有序集合，那么就返回一个错误。</li> <li>
	 * 分数的值必须是一个表示数字的字符串，并且可以是double类型的浮点数。</li>
	 * 
	 * 
	 * @param key
	 * @param score
	 * @param member
	 * @return 添加到有序集合的成员个数，不包括只更新分数的成员
	 */
	public boolean zadd(String key, double score, String member) {
		return zSetOperations.add(key, member, score);
	}

	/**
	 * 从key对应的有序集合中删除给定的成员。如果给定的成员不存在就忽略。
	 * 
	 * 
	 * @param key
	 * @param member
	 * @return 返回的是从有序集合中删除的成员个数，不包括不存在的成员。
	 */
	public long zrem(String key, String... member) {
		return zSetOperations.remove(key, member);
	}

	/**
	 * <p>
	 * 返回有序集key中成员member的排名。其中有序集成员按score值递增(从小到大)顺序排列。排名以0为底，也就是说，
	 * score值最小的成员排名为0。
	 * </p>
	 * 
	 * @param key
	 * @param member
	 * @return <li>如果member是有序集key的成员，返回member的排名的整数。</li> <li>
	 *         如果member不是有序集key的成员，返回Bulk reply: nil。</li>
	 */
	public long zrank(String key, String member) {
		return zSetOperations.rank(key, member);
	}

	/**
	 * <p>
	 * 返回有序集key中成员member的排名，其中有序集成员按score值从大到小排列。排名以0为底，也就是说，score值最大的成员排名为0。
	 * </p>
	 * 
	 * @param key
	 * @param member
	 * @return <li>如果member是有序集key的成员，返回member的排名。整型数字。</li><li>
	 *         如果member不是有序集key的成员，返回Bulk reply: nil.</li>
	 */
	public long zrevrank(String key, String member) {
		return zSetOperations.reverseRank(key, member);
	}

	/**
	 * <p>
	 * 返回有序集key中，指定区间内的成员。其中成员按score值递增(从小到大)来排序。具有相同score值的成员按字典序来排列。
	 * </p>
	 * 
	 * @param key
	 * @param start
	 * @param end
	 * @return
	 */
	public Set<Object> zrange(String key, long start, long end) {
		return zSetOperations.range(key, start, end);
	}

	/**
	 * 返回有序集key中，指定区间内的成员。其中成员的位置按score值递减(从大到小)来排列。具有相同score值的成员按字典倒序排列。
	 * 
	 * @param key
	 * @param start
	 * @param end
	 * @return
	 */
	public Set<Object> zrevrange(String key, long start, long end) {
		return zSetOperations.reverseRange(key, start, end);
	}

	/**
	 * 返回key的有序集元素个数。
	 * 
	 * 
	 * @param key
	 * @return key存在的时候，返回有序集的元素个数，否则返回0。
	 */
	public long zcard(String key) {
		return zSetOperations.zCard(key);
	}

	/**
	 * 返回有序集key中，成员member的score值。
	 * 
	 * 
	 * @param key
	 * @param member
	 * @return 如果member元素不是有序集key的成员，或key不存在，返回nil。
	 */
	public double zscore(String key, String member) {
		return zSetOperations.score(key, member);
	}

	/**
	 * 返回有序集key中，score值在min和max之间(默认包括score值等于min或max)的成员。
	 * 
	 * [min,max]
	 * 
	 * @param key
	 * @param min
	 * @param max
	 * @return 指定分数范围的元素个数。
	 */
	public long zcount(String key, double min, double max) {
		return zSetOperations.count(key, min, max);
	}

	/**
	 * 返回指定score访问内的有序集合
	 * 
	 * 
	 * @param key
	 * @param min
	 * @param max
	 * @return
	 */
	public Set<Object> zrangeByScore(String key, double min, double max) {
		return zSetOperations.rangeByScore(key, min, max);
	}

	/**
	 * 返回key的有序集合中的分数在max和min之间的所有元素（包括分数等于max或者min的元素）。与有序集合的默认排序相反，对于这个命令，
	 * 元素被认为是从高分到低具有相同分数的元素按字典反序。
	 * 
	 * @param key
	 * @param min
	 * @param max
	 * @return
	 */
	public Set<Object> zrevrangeByScore(String key, double min, double max) {
		return zSetOperations.reverseRangeByScore(key, min, max);
	}

	/**
	 * 移除有序集key中，所有score值介于min和max之间(包括等于min或max)的成员。
	 * 
	 * @param key
	 * @param start
	 * @param end
	 * @return 删除的元素的个数。
	 */
	public long zremrangeByScore(String key, double min, double max) {
		return zSetOperations.removeRangeByScore(key, min, max);
	}

	/**
	 * 移除有序集key中，所有排名值介于min和max之间(包括等于min或max)的成员。
	 * 
	 * @param key
	 * @param start
	 * @param end
	 * @return
	 */
	public Long zremrangeByScore(String key, long start, long end) {
		return zSetOperations.removeRange(key, start, end);
	}


	/**
	 * 获取通过正则可以匹配的key 
	 * 
	 * @param pattern
	 *            匹配模式
	 * @return
	 */
	public Set<String> keys(String pattern) {
		return redisTemplate.keys(pattern);
	}

	/**
	 * 移除Key的过期时间
	 * 
	 * @param key
	 * @return
	 */
	public boolean persist(String key) {
		return redisTemplate.persist(key);
	}
	
	public boolean setIfAbsent(String key, String value) {
		return valueOperations.setIfAbsent(key, value);
	}
	
	public String getAndSet(String key, String value) {
		return valueOperations.getAndSet(key, value);
	}
	
	
	/**
	 * Object转成JSON数据
	 */
	private String toJson(Object object) {
		if (object instanceof Integer || object instanceof Long
				|| object instanceof Float || object instanceof Double
				|| object instanceof Boolean || object instanceof String) {
			return String.valueOf(object);
		}
		return gson.toJson(object);
	}

	/**
	 * JSON数据，转成Object
	 */
	private <T> T fromJson(String json, Class<T> clazz) {
		return gson.fromJson(json, clazz);
	}
}
